package com.neusoft.neuiotms.dcom.service.alter.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import com.neusoft.bizcore.activiti.dto.ProcessInstanceDTO;
import com.neusoft.bizcore.activiti.service.ProcessInstanceService;
import com.neusoft.bizcore.activiti.service.WorkOrderService;
import com.neusoft.bizcore.auth.common.bean.UserBean;
import com.neusoft.bizcore.file.converter.MongoFileConverter;
import com.neusoft.bizcore.file.dto.FileDTO;
import com.neusoft.bizcore.file.model.FileDocument;
import com.neusoft.bizcore.file.service.FileService;
import com.neusoft.bizcore.web.constant.ExceptionConstants;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.neuiotms.dcom.constants.DCOMConstants;
import com.neusoft.neuiotms.dcom.converter.alter.AlterApplyFormConverter;
import com.neusoft.neuiotms.dcom.dto.alter.AlterApplyFormDTO;
import com.neusoft.neuiotms.dcom.model.Institutions;
import com.neusoft.neuiotms.dcom.model.OperationGroup;
import com.neusoft.neuiotms.dcom.model.alter.AlterApplyForm;
import com.neusoft.neuiotms.dcom.repository.InstitutionsRepository;
import com.neusoft.neuiotms.dcom.repository.OperationGroupRepository;
import com.neusoft.neuiotms.dcom.repository.alter.AlterApplyFormAnnexRepository;
import com.neusoft.neuiotms.dcom.repository.alter.AlterApplyFormRepository;
import com.neusoft.neuiotms.dcom.service.alter.AlterApplyFormService;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class AlterApplyFormServiceImpl implements AlterApplyFormService {

    @Autowired
    private AlterApplyFormConverter alterApplyFormConverter;
    @Autowired
    private AlterApplyFormRepository alterApplyFormRepository;
    @Autowired
    private WorkOrderService workOrderService;
    @Autowired
    private ProcessInstanceService processInstanceService;
    @Autowired
    private InstitutionsRepository institutionsRepository;
    @Autowired
    private OperationGroupRepository operationGroupRepository;
    @Autowired
    private FileService fileService;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private MongoFileConverter mongoFileConverter;
    @Autowired
    private GridFsTemplate gridFsTemplate;
    @Value("${files.storage.mongodb.collection:files}")
    private String collectionName;
    @Autowired
    private AlterApplyFormAnnexRepository alterApplyFormAnnexRepository;

    @Transactional
    @Override
    public AlterApplyFormDTO create(final AlterApplyFormDTO dto, final UserBean user) {

        final String taskId = dto.getTaskId();
        final String title = StringUtils.isNotEmpty(dto.getTitle()) ? dto.getTitle() : user.getName() + "申请了故障工单";

        //判断是否为暂存状态
        if (DCOMConstants.STR_TEMPORARY.equals(dto.getStatus())) {
            // 暂存工单
            final String workNum =
                    this.workOrderService.temporaryStorage(title, DCOMConstants.ALTER_PROCESS_KEY,
                            dto.getWorkNum(), user);
            dto.setWorkNum(workNum);
        } else {

            final ProcessInstanceDTO instance =
                    this.workOrderService.submit(title, DCOMConstants.ALTER_PROCESS_KEY, dto.getWorkNum(), user);

            // 设置当前任务ID
            dto.setTaskId(instance.getTaskId());
            // 设置工单号
            dto.setWorkNum(instance.getWorkorderNum());

            // 一线运维人员人员编码
            final String frontlineEngineerCode = dto.getFrontlineEngineerCode();

            // 设置一线运维人员审批
            this.processInstanceService.setGlobalVariable(instance.getTaskId(),
                    DCOMConstants.ALTER_PROCESS_EXECUTOR_FRONT_LINE_ENGINEER_CODE,
                    frontlineEngineerCode);

            // 设置参与工程师
            this.processInstanceService.setGlobalVariable(instance.getTaskId(),
                    DCOMConstants.ALTER_PROCESS_HELPER_ENGINEER_CODE,
                    StringUtils.isNotEmpty(dto.getHelperEngineerCode()) ? dto.getHelperEngineerCode() : "");
            this.processInstanceService.setGlobalVariable(instance.getTaskId(),
                    DCOMConstants.ALTER_PROCESS_HELPER_ENGINEER_NAME,
                    StringUtils.isNotEmpty(dto.getHelperEngineerName()) ? dto.getHelperEngineerName() : "");

            // 查找一线运维人员对应的组长
            final Institutions institutions =
                    this.institutionsRepository.findByResidents_UsernameIn(Arrays.asList(frontlineEngineerCode));
            if (null != institutions) {
                final List<Institutions> institutionList = new ArrayList<>();
                institutionList.add(institutions);
                final OperationGroup operationGroup =
                        this.operationGroupRepository.findByInstitutionsIn(institutionList);
                if (null != operationGroup) {
                    // 流程中设置组长的编码
                    this.processInstanceService.setGlobalVariable(instance.getTaskId(),
                            DCOMConstants.ALTER_PROCESS_EXECUTOR_OPERATION_TEAMLEADER,
                            operationGroup.getGroupLeaderCode());
                }
            }

            // 完成节点
            this.processInstanceService.complete(dto.getWorkNum(), instance.getTaskId(), user);
        }

        // 查询当前workNum和TaskId下是否存在记录
        final AlterApplyForm oldModel = this.alterApplyFormRepository.findByWorkNumAndTaskId(
                dto.getWorkNum(), taskId);
        // 如果不存在新增
        if (oldModel == null) {
            dto.setId(null);
        }

        final AlterApplyForm model = this.alterApplyFormConverter.toModel(dto);
        this.alterApplyFormRepository.save(model);

        return this.alterApplyFormConverter.toDTO(model);
    }

    @Override
    public AlterApplyFormDTO detail(final String workNum, final String taskId, final UserBean user) {

        AlterApplyForm model = null;
        try {
            if (StringUtils.isNotBlank(workNum) && !DCOMConstants.STR_NULL.equals(taskId)) {
                model = this.alterApplyFormRepository.findByWorkNumAndTaskId(workNum, taskId);
                if (model == null) {
                    // 获取前一节点的TaskID
                    final String fromProcessTaskId = this.processInstanceService.getLastTaskId(taskId, user);
                    if (fromProcessTaskId == null) {
                        return new AlterApplyFormDTO();
                    } else {
                        model = this.alterApplyFormRepository.findByWorkNumAndTaskId(workNum, fromProcessTaskId);
                    }
                }
            } else if (DCOMConstants.STR_NULL.equals(taskId)) {
                model = this.alterApplyFormRepository.findTopByWorkNumOrderByIdDesc(workNum);
            }
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND,
                    new Object[] {workNum },
                    new Object[] {taskId });
        }
        return this.alterApplyFormConverter.toDTO(model);
    }

    @Override
    public void deleteByWorkNum(final String workNum) {

        this.alterApplyFormRepository.deleteByWorkNum(workNum);

    }

    @Override
    public FileDTO save(final MultipartFile file) {
        try {
            final String md5 = DigestUtils.md5DigestAsHex(file.getInputStream());
            FileDocument doc = this.getByMd5(md5);
            if (doc != null) {
                return this.mongoFileConverter.toDTO(doc);
            }

            doc = new FileDocument();
            doc.setName(file.getOriginalFilename());
            doc.setSize(file.getSize());
            doc.setContentType(file.getContentType());
            doc.setUploadDate(new Date());
            doc.setMd5(md5);
            final String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
            doc.setSuffix(suffix);

            final String fsId = this.uploadFileToGridFS(file.getInputStream(), this.createFilePath(suffix), suffix);
            doc.setFsId(fsId);
            doc = this.mongoTemplate.save(doc, this.collectionName);
            return this.mongoFileConverter.toDTO(doc);
        } catch (final IOException e) {
            AlterApplyFormServiceImpl.log.error("", e);
        }
        return null;
    }

    @Transactional
    @Override
    public void deleteMongoFile(final String fsId) {
        this.fileService.deleteByFsId(fsId);
        this.alterApplyFormAnnexRepository.deleteByFsId(fsId);

    }

    public String uploadFileToGridFS(final InputStream in, final String fileName, final String contentType) {
        this.gridFsTemplate.store(in, fileName, contentType);
        return fileName;
    }

    private FileDocument getByMd5(final String md5) {
        final Query query = new Query().addCriteria(Criteria.where("md5").is(md5));
        final FileDocument fileDocument = this.mongoTemplate.findOne(query, FileDocument.class, this.collectionName);
        return fileDocument;
    }

    private String createFilePath(final String suffix) {
        final StringBuilder id = new StringBuilder();
        id.append(String.valueOf(System.currentTimeMillis()))
                .append(String.valueOf((int) (Math.random() * 10000)))
                .append(".")
                .append(suffix);
        return id.toString();
    }

}
